﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL4;


namespace Common {
    /// <summary>
    /// structure of array attrib manager.
    /// T must be member of VertexAttribPointerType assigned.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class VertexAttribManager<T> where T : unmanaged {
        internal class VboAttribsInfo {
            public VboAttribsInfo(VboBase vbo) {
                this.Vbo = vbo;
            }
            public VboBase Vbo { get; }
            public int TotalElementSize { get; set; }
            //public int CurrentElementOffset { get; set; }
        }
        public struct VertexAttrib {
            /// <summary>
            /// for example, a vector3 has 3 element of float
            /// </summary>
            public int ElementCount;
            public VertexAttribPointerType ElementType;
            public bool Normalized;
            internal VboAttribsInfo VboInfo;
            public int Offset;
        }

        int elementSize = GLUtility.SizeOf<T>();
        List<VertexAttrib> attribs = new List<VertexAttrib>(4);
        List<VboAttribsInfo> vboInfoList = new List<VboAttribsInfo>();
        //int totalElementSize = 0;

        VboAttribsInfo FindVboInfoAddIfNew(VboBase vbo) {
            foreach (var item in this.vboInfoList) {
                if (item.Vbo == vbo) {
                    return item;
                }

            }
            var newVboInfo = new VboAttribsInfo(vbo);
            vboInfoList.Add(newVboInfo);
            return newVboInfo;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="elementCount">for example, if element type is float, buffer contains 3 float vertex(x,y,z), then element count is 3
        /// if buffer contains  array, then element count is 1
        /// </param>
        /// <param name="vbo"></param>
        /// <param name="elementType"></param>
        /// <param name="normalized"></param>
        public void AddAttrib(int elementCount, VboBase vbo, VertexAttribPointerType elementType = VertexAttribPointerType.Float, bool normalized = false) {
            //foreach (var attrib in this.attribs) {
            //    if (vboInfo == attrib.VboInfo) {
            //        vboInfo.TotalElementSize += elementCount * elementSize;
            //    }
            //    else {
            //        vboInfo.TotalElementSize += elementCount * elementSize;
            //    }
            //}
            var vboInfo = FindVboInfoAddIfNew(vbo);
            //vboInfo.CurrentElementOffset = vboInfo.TotalElementSize;
            int currentOffset = vboInfo.TotalElementSize;
            vboInfo.TotalElementSize += elementCount * elementSize;
            attribs.Add(new VertexAttrib { ElementCount = elementCount, ElementType = elementType, Normalized = normalized, VboInfo = vboInfo, Offset = currentOffset });
            PointCount = vboInfo.Vbo.ByteLength / vboInfo.TotalElementSize;
        }
        public int PointCount { get; private set; }
        public void Enable() {

            for (int i = 0; i < attribs.Count; i++) {
                var vboInfo = attribs[i].VboInfo;
                GL.BindBuffer(vboInfo.Vbo.BufferTarget, vboInfo.Vbo.Id);
                //GL.VertexAttribPointer(i, attribs[i].ElementCount, attribs[i].ElementType, attribs[i].Normalized, vboInfo.TotalElementSize, vboInfo.CurrentElementOffset);
                GL.VertexAttribPointer(i, attribs[i].ElementCount, attribs[i].ElementType, attribs[i].Normalized, vboInfo.TotalElementSize, attribs[i].Offset);
                GL.EnableVertexAttribArray(i);
            }

        }
        public void Disable() {
            for (int i = 0; i < attribs.Count; i++) {
                GL.DisableVertexAttribArray(i);
            }
        }
        public void DisposeVbos() {
            foreach (var item in this.attribs) {
                item.VboInfo.Vbo.Dispose();
            }
        }
    }
    public class VertexAttribManager : VertexAttribManager<float> {
    }
}
